//! 四.利用封装好的CRUD封装对数据库的实际操作（第一步在Entity）

using TM.Api.Entity;
using TM.Api.Database;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace TM.Api.Repository
{

    /// <summary>
    /// 继承IRepository并实现，用于数据库的CRUD操作。
    /// </summary>
    /// <typeparam name="T">泛型，可以是任意一个实体类型(例如Users表)</typeparam>
    public class EfRepository<T> : IRepository<T> where T : BaseEntity
        //# 小Tips：实现IRepository接口点一下就好了哦~
    {


        /// <summary>
        /// _db:基础表
        /// </summary>
        public TMDB _db;


        //@ EfRepository:这里是一个构造函数。db是形参。
        public EfRepository(TMDB db)
        {
            _db = db;
        }



        /// <summary>
        /// _entity:实体表
        /// </summary>
        private DbSet<T> _entity;


        /// <summary>
        /// 判断是否有实体（表），若无，则使用基础实体（基础表）
        /// </summary>
        /// <value></value>
        protected DbSet<T> Entity
        //? Entity:此为下面所有Entity的隶属。

        {
            get
            {
                if (_entity == null)
                {
                    _entity = _db.Set<T>();
                }
                return _entity;
            }
        }

        public IQueryable<T> Table
        {
            get
            {
                //? AsQueryable:在数据库中查询再返回数据。
                return Entity.AsQueryable<T>();
            }
        }




        /// <summary>
        /// 根据提供的主键id，删除数据库中对应的记录
        /// </summary>
        /// <param name="id">提供的id</param>
        public void Delete(object id)
        {
            var x = Entity.Find(id);
            if (x == null)
            {
                //? 抛出异常。
                throw new ArgumentNullException(nameof(x));
            }
            _db.Remove(x);
            _db.SaveChanges();
        }




        public void DeleteBulk(IEnumerable<object> ids)
        {
            var x = new List<int>();
            foreach (var item in ids)
            {
                var tmp = (int)item;
                x.Add(tmp);
            }
            //? Contains:判断当前列表若是否包含某元素。这里是在y.Id里寻找与x相同的元素。
            var t = Entity.Where(y => x.Contains(y.Id));
            //? t为一个集合。

            _db.RemoveRange(t);
            _db.SaveChanges();
        }

        public T GetById(object id)
        {
            return Entity.Find(id);
        }

        public void Insert(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException();
            }
            entity.UpdateTime = DateTime.Now;
            Entity.Add(entity);
            _db.SaveChanges();
        }

        public async Task InsertAsync(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException();
            }
            entity.UpdateTime = DateTime.Now;
            await Entity.AddAsync(entity);
            await _db.SaveChangesAsync();
        }

        public void InsertBulk(IEnumerable<T> entities)
        {
            foreach (var entity in entities)
            {
                entity.UpdateTime = DateTime.Now;
            }
            Entity.AddRange(entities);
            _db.SaveChanges();
        }

        public async Task InsertBulkAsync(IEnumerable<T> entities)
        {
            foreach (var entity in entities)
            {
                entity.UpdateTime = DateTime.Now;
            }
            await Entity.AddRangeAsync(entities);
            await _db.SaveChangesAsync();
        }



        public void Update(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            entity.UpdateTime = DateTime.Now;
            _db.SaveChanges();
        }

        public void UpdateBulk(IEnumerable<T> entities)
        {
            foreach (var entity in entities)
            {
                entity.UpdateTime = DateTime.Now;
            }
            Entity.UpdateRange(entities);
            _db.SaveChanges();
        }
    }

}
//?  写完接口就可以去 Startup 里实现了
//!  实现完后就到第五步，控制器和路由。但再此之前，要写写一个小东西，去 Params 看看。
